package ws

import (
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"sync"
	"time"

	"gddgame.cc/galaxy/satellite"

	//"golang.org/x/net/websocket"
	"gddgame.cc/galaxy/utils/def"

	"github.com/gorilla/websocket"
)

var (
	connPool = sync.Pool{}
)

func init() {
	connPool.New = func() interface{} {
		return &webSocketConn{
			mutex: sync.Mutex{},
		}
	}
}

type HttpMux interface {
	Handle(pattern string, handler http.Handler)
}
type webSocket struct {
	def.Logger

	path      string
	http      HttpMux
	u         *url.URL
	connected bool
	accept    chan satellite.Conn
}
type webSocketConn struct {
	*websocket.Conn
	reader      io.Reader
	writer      io.WriteCloser
	messageType int
	mutex       sync.Mutex
	connected   bool
	transport   satellite.Transport
}

var upgrader = websocket.Upgrader{} // use default options
type Handler func(conn *websocket.Conn)

func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	//conn, rw, hs, err := ws.UpgradeHTTP(r, w, nil)
	//if err != nil {
	//	fmt.Println(err)
	//} else {
	//	h(conn, rw, hs)
	//}
	c, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		fmt.Println(err)
	} else {
		h(c)
	}
}

func NewTransportServer(path string, http HttpMux) *webSocket {
	transport := &webSocket{
		path: path,
		http: http,
	}
	return transport
}

func NewTransportClient(u *url.URL) *webSocket {
	transport := &webSocket{
		u: u,
	}
	return transport
}

func (transport *webSocket) Dial() (satellite.Conn, error) {
	conn, _, err := websocket.DefaultDialer.Dial(transport.u.String(), nil)
	if err != nil {
		return nil, err
	}
	c := connPool.Get().(*webSocketConn)
	c.Conn = conn
	c.transport = transport
	return c, err
}

func (transport *webSocket) Listen() (<-chan satellite.Conn, error) {
	transport.accept = make(chan satellite.Conn)
	handler := func(conn *websocket.Conn) {
		transport.connected = true
		//if socket.logger != nil {
		//	socket.logger.Debugf("websocket new conn:%#v", conn)
		//}
		if transport.accept == nil {
			//fmt.Println("ws accept close")
			return
		}
		if transport.Logger != nil {
			transport.Debugf("websocket new conn")
		}

		c := connPool.Get().(*webSocketConn)
		c.transport = transport
		c.Conn = conn
		c.connected = true
		transport.accept <- c
	}
	transport.http.Handle(transport.path, Handler(handler))
	if transport.Logger != nil {
		transport.Debugf("Listen websocket: %#v", transport.path)
	}
	return transport.accept, nil
}
func (transport *webSocket) Type() satellite.TransportType {
	return "ws"
}
func (transport *webSocket) Schema() string {
	if transport.u == nil {
		return transport.path
	} else {
		return transport.u.String()
	}
}
func (transport *webSocket) SetLogger(logger def.Logger) {
	transport.Logger = logger
}

func (transport *webSocket) Close() error {
	//fmt.Println("close ws")
	transport.accept = nil
	return nil
}

func (transport *webSocket) Connected() bool {
	return transport.connected
}
func (c *webSocketConn) Transport() satellite.Transport {
	return c.transport
}
func (c *webSocketConn) Close() error {
	var err error
	c.connected = false
	if c.Conn != nil {
		err = c.Conn.Close()
		c.Conn = nil
	}
	c.reader = nil
	c.writer = nil
	c.messageType = 0
	connPool.Put(c)
	return err
}
func (c *webSocketConn) Read(b []byte) (n int, err error) {
	if c.reader == nil {
		if _, err := c.Reader(); err != nil {
			if err == io.ErrUnexpectedEOF {
				return 0, io.EOF
			}
			return 0, err
		}
	}
	return c.reader.Read(b)
}

func (c *webSocketConn) Write(b []byte) (n int, err error) {
	if c.writer == nil {
		if _, err := c.StartWrite(); err != nil {
			return 0, err
		}
	}
	return c.writer.Write(b)
}

func (c *webSocketConn) SetDeadline(t time.Time) error {
	err := c.SetReadDeadline(t)
	if err != nil {
		return err
	}
	err = c.SetWriteDeadline(t)
	if err != nil {
		return err
	}
	return nil
}
func (c *webSocketConn) Reader() (io.Reader, error) {
	var err error
	c.messageType, c.reader, err = c.Conn.NextReader()
	if err != nil {
		// 不同的关闭错误处理
		if _, ok := err.(*websocket.CloseError); ok {
			return nil, io.EOF
		}
		if _, ok := err.(net.Error); ok {
			return nil, io.EOF
		}
		return nil, err
	}
	return c.reader, err
}

func (c *webSocketConn) StartWrite() (io.Writer, error) {
	var err error
	c.mutex.Lock()
	c.writer, err = c.Conn.NextWriter(websocket.BinaryMessage)
	if err != nil {
		c.mutex.Unlock()
		c.writer = nil
		return nil, err
	}
	return c.writer, err
}

func (c *webSocketConn) EndWrite() {
	if c.writer != nil {
		_ = c.writer.Close()
		c.writer = nil
		c.mutex.Unlock()
	}
}

func (c *webSocketConn) ReadData(header []byte) (io.Reader, error) {
	messageType, reader, err := c.Conn.NextReader()
	if err != nil {
		// 不同的关闭错误处理
		if _, ok := err.(*websocket.CloseError); ok {
			return nil, io.EOF
		}
		if _, ok := err.(net.Error); ok {
			return nil, io.EOF
		}
		return nil, err
	}
	c.messageType = messageType
	if header == nil {
		return reader, nil
	}
	_, err = reader.Read(header)
	return reader, err
}

func (c *webSocketConn) WriteData(header []byte, buffer io.WriterTo) error {
	c.mutex.Lock()
	writer, err := c.Conn.NextWriter(websocket.BinaryMessage)
	if err != nil {
		fmt.Println(err)
		c.mutex.Unlock()
		return err
	}
	_, err = writer.Write(header)
	if err == nil {
		_, err = buffer.WriteTo(writer)
		_ = writer.Close()
	} else {
		err = writer.Close()
	}
	c.mutex.Unlock()
	return err
}

func (c *webSocketConn) Connected() bool {
	return c.connected
}
